﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Ndo.Maths.MOP
{
    public static class BenchMarks
    {
        public static readonly Type[] All = new Type[]
            {
                typeof(SCH),
                typeof(FON),
                typeof(POL),
                typeof(KUR),
                typeof(SPT),
                typeof(ZDT1),
                typeof(ZDT2),
                typeof(ZDT3),
                typeof(ZDT4),
                typeof(ZDT6),
            };
        public static BenchMark<double[]> FromName(string name)
        {
            foreach (Type type in All)
            {
                if (type.Name == name)
                    return (BenchMark<double[]>) type.GetConstructor(new Type[0]).Invoke(new object[0]);
            }
            return null;
        }
        public static double[] PositiveInfinityArray(int n)
        {
            double[] ret = new double[n];
            while (--n >= 0)
                ret[n] = double.PositiveInfinity;
            return ret;
        }
    }

    public class SCH : BenchMark<double[]>
    {
        public SCH()
        {
            LowerBound = new double[] { -1000 };
            UpperBound = new double[] { 1000 };
        }
        public override double[] F(double[] x)
        {
            if (!Check(x))
                return BenchMarks.PositiveInfinityArray(YDimension);
            double[] y = new double[2];
            y[0] = x[0] * x[0];
            y[1] = (x[0] - 2);
            y[1] *= y[1];
            return y;
        }
        public override double[][] XOptimums(int c)
        {
            double[][] xs = new double[c][];
            int n1 = c - 1;
            for(int i=0; i<c; i++)
            {
                xs[i] = new double[]{(i*2.0)/n1};
            }
            return xs;
        }
    }

    public class FON : BenchMark<double[]>
    {
        public FON()
            :this(3)
        {}
        public FON(int n)
        {
            LowerBound = new double[n];
            UpperBound = new double[n];
            while(--n>=0)
            {
                LowerBound[n] = -4;
                UpperBound[n] = 4;
            }
        }

        public static readonly double a = 1/Math.Sqrt(3);
        public override double[] F(double[] x)
        {
            if (!Check(x))
                return BenchMarks.PositiveInfinityArray(YDimension);
            double[] y = new double[2];
            foreach (double d in x)
            {
                y[0] -= (d - a)*(d - a);
                y[1] -= (d + a)*(d + a);
            }
            y[0] = 1 - Math.Exp(y[0]);
            y[1] = 1 - Math.Exp(y[1]);
            return y;
        }
        public override double[][] XOptimums(int c)
        {
            double[][] xs = new double[c][];
            int n1 = c - 1;
            for (int i = 0; i < c; i++)
            {
                double d = (i*2)*a/n1 - a;
                xs[i] = new double[LowerBound.Length];
                for(int j=0;j<LowerBound.Length; j++)
                    xs[i][j] = d;
            }
            return xs;
        }
    }


    public class POL : BenchMark<double[]>
    {
        public POL()
        {
            LowerBound = new double[] { -Math.PI, -Math.PI };
            UpperBound = new double[] { Math.PI, Math.PI };
        }

        public static readonly double A1 = 0.5 * Math.Sin(1) - 2 * Math.Cos(1) + Math.Sin(2) - 1.5 * Math.Cos(2);
        public static readonly double A2 = 1.5 * Math.Sin(1) - Math.Cos(1) + 2 * Math.Sin(2) - 0.5 * Math.Cos(2);
        public override double[] F(double[] x)
        {
            if (!Check(x))
                return BenchMarks.PositiveInfinityArray(YDimension);
            double[] y = new double[2];
            double B1 = A1 - (0.5 * Math.Sin(x[0]) - 2 * Math.Cos(x[0]) + Math.Sin(x[1]) - 1.5 * Math.Cos(x[1])),
                   B2 = A2 - (1.5 * Math.Sin(x[0]) - Math.Cos(x[0]) + 2 * Math.Sin(x[1]) - 0.5 * Math.Cos(x[1]));
            y[0] = 1 + B1*B1 + B2*B2;
            y[1] = (x[0] + 3)*(x[0] + 3) + (x[1] + 1)*(x[1] + 1);
            return y;
        }
    }

    public class KUR: BenchMark<double[]>
    {
        public KUR()
        {
            LowerBound = new double[] { -5, -5, -5 };
            UpperBound = new double[] { 5, 5, 5 };
        }

        public override double[] F(double[] x)
        {
            if (!Check(x))
                return BenchMarks.PositiveInfinityArray(YDimension);
            double[] y = new double[2];
            y[0] = -10*Math.Exp(-0.2*Math.Sqrt(x[0]*x[0] + x[1]*x[1]))
                   - 10*Math.Exp(-0.2*Math.Sqrt(x[2]*x[2] + x[1]*x[1]));
            y[1] = Math.Pow(Math.Abs(x[0]), 0.8) + 5*Math.Sin(x[0]*x[0]*x[0]) +
                   Math.Pow(Math.Abs(x[1]), 0.8) + 5*Math.Sin(x[1]*x[1]*x[1]) +
                   Math.Pow(Math.Abs(x[2]), 0.8) + 5*Math.Sin(x[2]*x[2]*x[2]);
            return y;
        }
    }

    public class SPT : BenchMark<double[]>
    {
        public SPT()
        {
            LowerBound = new double[] { 0, 0, 0 };
            UpperBound = new double[] { 10, 10, 10 };
        }
        public override double[] F(double[] x)
        {
            if (!Check(x))
                return BenchMarks.PositiveInfinityArray(YDimension);
            if (x[0] >= 0 && x[1] >= 0 && x[2] >= 0)
            {
                double t = x[0] + x[1] + x[2];
                if (t >= 3)
                    return (double[])x.Clone();
            }
            return new double[] { 100, 100, 100 };
        }
        public override double[][] XOptimums(int c)
        {
            if(c<8)
                return new double[6][]
                {
                    new double[]{3,0,0}, 
                    new double[]{0,3,0}, 
                    new double[]{0,0,3}, 
                    new double[]{1.5,1.5,0}, 
                    new double[]{1.5,0,1.5}, 
                    new double[]{0,1.5,1.5}, 
                };
            int n = (int) Math.Sqrt(c*2);
            double[][] xs = new double[n * (n + 1) / 2][];
            for (int i = 0, index = 0; i < n; i++)
            {
                double d1 = 3.0 - i*3.0/(n - 1);
                for (int j = 0; j <= i; j++)
                {
                    double d2 = j*3.0/(n - 1);
                    xs[index++] = new double[3] { d1, d2, 3.0 - d1 - d2 };
                }
            }
            return xs;
        }
    }

    public abstract class ZDT : BenchMark<double[]>
    {
        public ZDT(int n)
        {
            LowerBound = new double[n];
            UpperBound = new double[n];
            while(--n>=0)
            {
                LowerBound[n] = 0;
                UpperBound[n] = 1;
            }
        }
        public override double[][] XOptimums(int c)
        {
            int m = LowerBound.Length, n1 = c - 1;
            double[][] ret = new double[c][];
            for(int i=0; i<c; i++)
            {
                double[] ds = new double[m];
                ds[0] = (double) i/n1;
                ret[i] = ds;
            }
            return ret;
        }
    }

    public class ZDT1 : ZDT
    {
        public ZDT1() : base(30) { }
        public ZDT1(int n) : base(n) { }

        public override double[] F(double[] x)
        {
            if (!Check(x))
                return BenchMarks.PositiveInfinityArray(YDimension);
            double[] y = new double[2];
            y[0] = x[0];
            double g = 0;
            for (int i = 1; i < x.Length; i++)
                g += x[i];
            g = 1 + 9 * g / (x.Length - 1);
            y[1] = g * (1 - Math.Sqrt(x[0] / g));
            return y;
        }
    }
    public class ZDT2 : ZDT
    {
        public ZDT2() : base(30) { }
        public ZDT2(int n) : base(n) { }

        public override double[] F(double[] x)
        {
            if (!Check(x))
                return BenchMarks.PositiveInfinityArray(YDimension);
            double[] y = new double[2];
            y[0] = x[0];
            double g = 0;
            for (int i = 1; i < x.Length; i++)
                g += x[i];
            g = 1 + 9 * g / (x.Length - 1);
            y[1] = g * (1 - (x[0] / g) * (x[0] / g));
            return y;
        }
    }
    public class ZDT3 : ZDT
    {
        public ZDT3() : base(30) { }
        public ZDT3(int n) : base(n) { }

        public override double[] F(double[] x)
        {
            if (!Check(x))
                return BenchMarks.PositiveInfinityArray(YDimension);
            double[] y = new double[2];
            y[0] = x[0];
            double g = 0;
            for (int i = 1; i < x.Length; i++)
                g += x[i];
            g = 1 + 9 * g / (x.Length - 1);
            y[1] = g * (1 - Math.Sqrt(x[0] / g) - x[0]*Math.Sin(10*Math.PI*x[0])/g);
            return y;
        }
    }
    public class ZDT4 : ZDT
    {
        public ZDT4() : this(10) { }
        public ZDT4(int n) : base(n)
        {
            for(int i=1; i<n; i++)
            {
                LowerBound[i] = -5;
                UpperBound[i] = 5;
            }
        }

        public override double[] F(double[] x)
        {
            if (!Check(x))
                return BenchMarks.PositiveInfinityArray(YDimension);
            double[] y = new double[2];
            y[0] = x[0];
            double g = 1 + 10 * (x.Length - 1);
            for (int i = 1; i < x.Length; i++)
                g += x[i]*x[i] - 10*Math.Cos(4*Math.PI*x[i]);
            y[1] = g * (1 - Math.Sqrt(x[0] / g));
            return y;
        }
    }
    public class ZDT6 : ZDT
    {
        public ZDT6() : base(10) { }
        public ZDT6(int n) : base(n) { }

        public override double[] F(double[] x)
        {
            if (!Check(x))
                return BenchMarks.PositiveInfinityArray(YDimension);
            double[] y = new double[2];
            double g = 0;
            y[0] = 1 - Math.Exp(-4 * x[0]) * Math.Pow(Math.Sin(4 * Math.PI * x[0]), 6);
            for (int i = 1; i < x.Length; i++)
                g += x[i];
            g = 1 + 9 * Math.Pow(g / (x.Length - 1), 0.25);
            y[1] = g * (1 - (y[0] / g) * (y[0] / g));
            return y;
        }
    }

}
